1   /************************************************************
2   *                     Copyright                            *
3   * Portions of this software are Copyright (c) 1993 - 2002, *
4   * Chad Z. Hower (Kudzu) and the Indy Pit Crew              *
5   *  - http://www.nevrona.com/Indy/                          *
6   ************************************************************/
7   package org.indy;
8   
9   import java.net.InetAddress;
10  import java.net.UnknownHostException;
11  
12  import java.text.MessageFormat;
13  
14  import java.util.Collection;
15  import java.util.HashSet;
16  import java.util.Iterator;
17  
18  import org.indy.io.IndyUnknownHostException;
19  
20  
21  /***
22   * <code>IndyComponent</code> is the ancestor of all Indy components
23   * which implement client or server functionality.<p>
24   *
25   * <code>IndyComponent</code> provides a convinient method to acess the local
26   * machine's host name with {@link getLocalName()}, and is also responsible for
27   * firing events to registered instances of {@link IndyComponentListener} to signal state change, input,
28   * and output operations in the component.
29   *
30   *@author    OTG
31   *@version   1.0
32   *@see IndyComponentListener
33   */
34  public abstract class IndyComponent extends BaseComponent {
35    /***
36     * Cached version of local host name for internal use
37     */
38    private static String cachedHostName;
39  
40    /***
41     * Internal collection to hold component listeners. Currently uses a hash set to prevent duplicates.
42     */
43    private Collection listeners = new HashSet();
44  
45    /***
46     * Internal array of workInfos, to be the same size as there are elements in {@link WorkMode}.
47     * Used to support nesting of work events.
48     *
49     * @todo Find a better way of doing this?
50     */
51    private final WorkInfo[] workInfos = { new WorkInfo(), new WorkInfo() };
52  
53    /***
54     *  Returns the local host name of this machine.
55     *  <p>
56     *  NOTE: This value is lazily created and then cached, so that, at present,
57     *  changes to the host name will not update.
58     *  <p>
59     *  This method provides a shortcut for
60     *  <pre>
61     *     java.net.InetAddress.getLocalHost().getHostName();
62     *  </pre>
63     *
64     *@return    The host name of the local machine.
65     *@throws    IndyUnknownHostException if, for some reason, the local host can not be resolved
66     *@throws    SecurityExeption if a security manager exists and <code>checkConnect</code> doesn't allow this operation.
67     */
68    public final static String getLocalName() throws IndyUnknownHostException {
69      try {
70        if (cachedHostName == null) {
71          cachedHostName = InetAddress.getLocalHost().getHostName();
72        }
73      }
74       catch (UnknownHostException ex) {
75        throw new IndyUnknownHostException(ex);
76      }
77  
78      return cachedHostName;
79    }
80  
81    /***
82     *  Returns this instance's <code>workInfo</code> object for the appropriate work mode.
83     *
84     *@param  workMode  The work mode to retreive the workInfo for
85     *@return           The workInfo in question.
86     */
87    private WorkInfo workInfoForType(WorkMode workMode) {
88      return workInfos[workMode.type()];
89    }
90  
91    /***
92     *  Fires a status event with the given {@link Status}.
93     *
94     *  This is a shortcut for
95     *  <pre>
96     *     doStatus(status, new Object[0]);
97     *  </pre>
98     *
99     *@param  status  The <code>Status</code> that represents the state being changed to.
100    *@see    Status
101    */
102   protected final void doStatus(Status status) {
103     doStatus(status, new Object[0]);
104   }
105 
106   /***
107    *  Fires a state change event for a given {@link Status},
108    *  taking an array of arguments for message formatting.
109    *
110    *  The message formatting uses java.text.MessageFormat, and the appropriate resource string
111    *  for the <code>Status</code> in question.
112    *
113    *@param  status  The <code>Status</code> being changed to.
114    *@param  args    Arguments to format the state change message with.
115    */
116   protected final void doStatus(Status status, Object[] args) {
117     String msg = MessageFormat.format(status.toString(), args);
118 
119     Iterator i = listeners.iterator();
120 
121     while (i.hasNext()) {
122       ((IndyComponentListener) i.next()).onStatus(this, status, msg);
123     }
124   }
125 
126   /***
127    * Registers an {@link IndyComponentListener} to receive events from this component instance.
128    *
129    *@param  listener  The <code>IndyComponentListener</code> to receive events from this component.
130    *@see IndyComponentListener
131    */
132   public void addComponentListener(IndyComponentListener listener) {
133     listeners.add(listener);
134   }
135 
136   /***
137    *  Unregisters an {@link IndyComponentListener} from this component.
138    *
139    *@param  listener  The <code>IndyComponentListener</code> to remove.
140    *@see IndyComponentListener
141    */
142   public void removeComponentListener(IndyComponentListener listener) {
143     listeners.remove(listener);
144   }
145 
146   /***
147    *  Called to indicate the beginning of work of the given {@link WorkMode}.
148    *  Calls can be nested, but each begin must have a corresponding end.
149    *
150    *  Only the first beginWork in any nested set for a given <code>WorkMode</code> will fire a {@link IndyComponentListener#onBeginWork(IndyComponent,WorkMode,int)}
151    *  event.
152    *
153    *@param  workMode  The <code>WorkMode</code> of this work.
154    *@param  size      The size of the work - viz. how many bytes are being read, written or processed.
155    *@see doBeginWork(WorkMode)
156    *@see doEndWork(WorkMode)
157    *@see doWork(WorkMode,int)
158    *@see IndyComponentListener
159    *@see IndyComponentListener#onBeginWork(IndyComponent,WorkMode,int)
160    */
161   protected void doBeginWork(WorkMode workMode, int size) {
162     if (!(workMode.type() > workInfos.length - 1)) {
163       workInfoForType(workMode).level++;
164 
165       if (workInfoForType(workMode).level == 1) {
166         workInfoForType(workMode).max = size;
167         workInfoForType(workMode).current = 0;
168 
169         Iterator i = listeners.iterator();
170 
171         while (i.hasNext()) {
172           ((IndyComponentListener) i.next()).onBeginWork(this, workMode, size);
173         }
174       }
175     }
176   }
177 
178   /***
179    *  Calls to signal the beginning of work of a given {@link WorkMode}.
180    *
181    *  See {@link doBeginWork(WorkMode,int} for more details.
182    *
183    *  This method is a shortcut for
184    *  <pre>
185    *    doBeginWork(workMode,0);
186    *  </pre>
187    *
188    *@param  workMode  The <code>WorkMode</code> describing the type of operation underway.
189    *@see doBeginWork(WorkMode,int)
190    *@see doEndWork(WorkMode)
191    *@see doWork(WorkMode,int)
192    *@see IndyComponentListener
193    *@see IndyComponentListener#onBeginWork(IndyComponent,WorkMode,int)
194    */
195   protected final void doBeginWork(WorkMode workMode) {
196     doBeginWork(workMode, 0);
197   }
198 
199   /***
200    *  Signals that a piece of work of the given {@link WorkMode} is underway
201    *  to the tune of <code>count</code> bytes.
202    *
203    *  This call will have no effect unless a corresponding call to begin work has been made;
204    *  otherwise a {@link IndyComponentListener#onWork(IndyComponent, WorkMode, int) onWork}
205    *  event will be fired to all interested parties.
206    *
207    *@param  workMode  The <code>WorkMode</code> that this work represents.
208    *@param  count     The number of bytes being processed.
209    *@see doBeginWork(WorkMode,int)
210    *@see doEndWork(WorkMode,int)
211    *@see WorkMode
212    *@see IndyComponentListener
213    *@see IndyComponentListener#onWork(IndyComponent, IndyComponent.WorkMode, int)
214    *@todo Should this throw exceptions if count is > workInfo.max? Or if there has been no doBeginWork call?
215    */
216   protected final void doWork(WorkMode workMode, int count) {
217     if (!(workMode.type() > workInfos.length - 1)) {
218       if (workInfoForType(workMode).level > 0) {
219         workInfoForType(workMode).current += count;
220 
221         Iterator i = listeners.iterator();
222 
223         while (i.hasNext()) {
224           ((IndyComponentListener) i.next()).onWork(this, workMode, count);
225         }
226       }
227     }
228   }
229 
230   /***
231    *  Signals that a piece of work begun by {@link doBeginWork(WorkMode,int) doBeginWork}
232    *  has ended.
233    *
234    *  Each call to <code>doBeginWork</code> should have a corresponding call to <code>doEndWork</code>,
235    *  and, as with <code>doBeginWork</code> only the innovation representing the outermost portion of
236    *  work in a nested call will trigger an event, in this case {@link IndyComponentListener#onEndWork(IndyComponent,WorkMode)}.
237    *
238    *@param  workMode  The <code>WorkMode</code> for which work is ending.
239    *@see doBeginWork(WorkMode,int)
240    *@see doBeginWork(WorkMode)
241    *@see doWork(WorkMode,int)
242    *@see WorkMode
243    *@see IndyComponentListener
244    *@see IndyComponentListener#onEndWork(IndyComponent,WorkMode)
245    */
246   protected final void doEndWork(WorkMode workMode) {
247     if (!(workMode.type() > workInfos.length - 1)) {
248       if (workInfoForType(workMode).level == 1) {
249         Iterator i = listeners.iterator();
250 
251         while (i.hasNext()) {
252           ((IndyComponentListener) i.next()).onEndWork(this, workMode);
253         }
254       }
255 
256       workInfoForType(workMode).level--;
257     }
258   }
259 
260   /***
261    *  An enumerated class representing the possible work types for siganalling work events
262    *  in an <code>IndyComponent<code>. <p>
263    *
264    *@author    OTG
265    *@version   1.0
266    *@see IndyComponent
267    *@see IndyComponent#doBeginWork(WorkMode,int)
268    *@see IndyComponent#doBeginWork(WorkMode)
269    *@see IndyComponent#doWork(WorkMode,int)
270    *@see IndyComponent#doEndWork(WorkMode)
271    *@see IndyComponentListener
272    *@see IndyComponentListener#onBeginWork(IndyComponent,WorkMode,int)
273    *@see IndyComponenyListener#onWork(IndyComponent,WorkMode,int)
274    *@see IndyComponenyListener#onEndWork(IndyComponent,WorkMode)
275    */
276   public static final class WorkMode {
277     /***
278      *  The component is reading data from the peer connection.
279      */
280     public final static WorkMode READ = new WorkMode(0);
281 
282     /***
283      *  The component is writing data to the peer connection
284      */
285     public final static WorkMode WRITE = new WorkMode(1);
286     private int _type;
287 
288     private WorkMode(int type) {
289       _type = type;
290     }
291 
292     /***
293      *  A hack to allow the use of the workInfo[] for tracking work.
294      */
295     private int type() {
296       return _type;
297     }
298   }
299 
300   /***
301    * Used internally in {@link IndyComponent} for tracking calls to <code>doBeginWork</code>,<code>doWork</code> and <code>doEndWork</code>
302    *
303    * @author OTG
304    * @version 1.0
305    */
306   private class WorkInfo {
307     /***
308      * The current bytes worked upon in this chunk of work
309      */
310     private int current;
311 
312     /***
313      * The nesting level of this chunk of work
314      */
315     private int level;
316 
317     /***
318      * The overall number of bytes associated with this chunk of work
319      */
320     private int max;
321   }
322 }
This page was automatically generated by Maven